# -*- coding: utf-8 -*-
"""
@Time ： 2024/12/9 14:59
@Auth ： 周瑞东 Ryan.Zhou
@File ：hal_serial.py
"""
import time
import threading
import serial
import serial.tools.list_ports
from utils.global_state import GlobalState

class HALSerial:
    _instance = None

    def __new__(cls):
        """确保只有一个串口实例"""
        if cls._instance is None:
            cls._instance = super(HALSerial, cls).__new__(cls)
            cls._instance.serial_port_handler = None
            cls._instance.__global_state = GlobalState()
        return cls._instance

    def __init__(self):
        if not hasattr(self, '_initialized'):  # 确保只初始化一次
            self.serial_port_handler = None
            self.PowerSupply_addr = 0x01
            self.lock = threading.Lock()  # Create a Lock instance
            # Power on/off register(R/W)
            self._REG_ONOFF = 2016
            # Reference voltage register(R/W)
            self._REG_REF_V = 2001
            self._REG_STATE = 1007
            # Reference current register(R/W)
            self._REG_REF_I = 2002
            self.cmd_power_on = [0xff, 0xff]
            self.cmd_power_off = [0x00, 0x00]
            # 定义状态位的含义
            self.STATUS_MAPPING = {
                0: "Shot_Circuit",  # 是否短路
                1: "Temperature_High",  # 温度是否过高
                2: "Over_Current",  # 是否过流
                3: "Over_Voltage",  # 是否过压
                4: "Communication_Mode_Serial",
                5: "Constant_Current",  #
                6: "Constant_Voltage",  # 系统是否有警告
                7: "Device_Running"  # 是否处于故障状态
            }

            self._initialized = True

    # 监控连接是否正常，返回 True 或 False
    def monitor_connection(self):
        """定时监控连接状态，并执行自动重连或断开处理"""
        if self.serial_port_handler and self.serial_port_handler.is_open:
            # 串口正常连接时，只需检测是否已断开
            try:
                self.serial_port_handler.in_waiting  # 尝试访问，若失败则判断为断开
            except serial.SerialException:
                print("检测到串口断开")
                self.disconnect()
                return False
            else:
                return True
        else:
            # 若未连接，则尝试重新连接
            print("查找串口...")
            return self.connect()

    def find_target_port(self):
        """查找目标串口设备的端口号"""
        ports = serial.tools.list_ports.comports()
        for port in ports:
            if (port.vid == self.__global_state.power.target_vid and
                    port.pid == self.__global_state.power.target_pid and
                    port.serial_number == self.__global_state.power.target_serial_number):
                print(f"Successfully detected serial port > {port.device}")
                return port.device  # 返回端口号
        # print(f"No serial port with the specified serial number detected !!!")
        return None

    def disconnect(self):
        """断开连接并释放资源"""
        if self.serial_port_handler and self.serial_port_handler.is_open:
            self.serial_port_handler.close()
            self.serial_port_handler = None

    def connect(self):
        """尝试连接目标串口"""
        port_name = self.find_target_port()
        if port_name:
            try:
                self.serial_port_handler = serial.Serial(
                    port=port_name,
                    timeout=0.1
                )
            except serial.SerialException as e:
                print(f"连接失败: {e}")
                return False
            else:
                return True
        else:
            print("未找到目标串口")
            return False

    def decimal_to_hex_2bytes(self, decimal):
        if not (0 <= decimal <= 65535):
            raise ValueError("Decimal value must be between 0 and 65535")
        high_byte = (decimal >> 8) & 0xFF
        low_byte = decimal & 0xFF
        return [high_byte, low_byte]

    def add_crc(self, data):
        crc = 0xFFFF
        polynomial = 0xA001
        data_bytes = bytes(data)
        for byte in data_bytes:
            crc ^= byte
            for i in range(8):
                if crc & 0x0001:
                    crc >>= 1
                    crc ^= polynomial
                else:
                    crc >>= 1
        crc = [crc & 0xFF, (crc >> 8) & 0xFF]
        data_with_crc = data + crc
        return data_with_crc

    # 设定输出电压
    def set_vol_cur(self, vol, cur):
        with self.lock:
            if 0 < vol < 200 and 0 < cur < 15:
                vol = int(vol * 10)
                cur = int(cur * 100)

                voltage = self.decimal_to_hex_2bytes(vol)
                current = self.decimal_to_hex_2bytes(cur)

                self.write_reg(self._REG_REF_V, 2, (voltage + current))
                time.sleep(0.1)
                response = self.read()
                received_data = list(response)
                # 启动成功
                if len(received_data) != 0:
                    return True
                # 启动失败
                else:
                    return False

            # print('[{}]'.format(', '.join(f'0x{x:02X}' for x in EEE)))
        # 配置基准电压示例，电压不能超过200V，电流不能超过15A，否则配置无效
        # voltage = decimal_to_hex_2bytes(12 * 10)
        # current = decimal_to_hex_2bytes(5 * 100)
        # EEE = read_write_REG(_REG_REF_V, 2, (voltage + current))
        # print('[{}]'.format(', '.join(f'0x{x:02X}' for x in EEE)))

    # 查询输出电流
    def poll_output_cur(self):
        with self.lock:
            try:
                bytes = self.read_reg(1001, 2)
                result = (bytes[3] << 8) | bytes[4]
                cur = round(result / 100 , 1)
            except Exception as e:
                print(f"error in poll_output_cur: {e}")
                return None
            else:
                return cur
            # print('[{}]'.format(', '.join(f'0x{x:02X}' for x in DDD)))
            # 电压除以10，电流除以100

    def poll_output_vol(self):
        with self.lock:
            try:
                bytes = self.read_reg(1000, 2)
                result = (bytes[3] << 8) | bytes[4]
                vol = round(result / 10 , 1)
            except Exception as e:
                print(f"error in poll_output_vol: {e}")
                return None
            else:
                return vol
            # print('[{}]'.format(', '.join(f'0x{x:02X}' for x in DDD)))
            # 电压除以10，电流除以100

    def poll_state(self):
        with self.lock:
            try:
                bytes = self.read_reg(self._REG_STATE, 2)
                # print("bytes len", len(bytes))
                if len(bytes) < 5:
                    raise ValueError("接收数据长度不足")
                status_byte = bytes[4]
            except Exception as e:
                # print(f"error in poll_state: {e}")
                return None
            else:
                """  
                解析状态字节  
                :param status_byte: 包含状态信息的单个字节  
                :return: 详细的状态字典  
                """
                status_details = {}

                for bit in range(8):
                    # 使用位运算提取每个位的状态
                    bit_status = bool(status_byte & (1 << bit))
                    status_details[self.STATUS_MAPPING[bit]] = bit_status

                return status_details

    def power_on(self):
        with self.lock:
            self.write_reg(self._REG_ONOFF, 1, self.cmd_power_on)
            time.sleep(0.1)
            response = self.read()
            received_data = list(response)
            # 启动成功
            if len(received_data) != 0:
                return True
            # 启动失败
            else:
                return False

    def power_off(self):
        with self.lock:
            self.write_reg(self._REG_ONOFF, 1, self.cmd_power_off)
            time.sleep(0.1)
            response = self.read()
            received_data = list(response)
            if len(received_data) != 0:
                return True
            else:
                return False

    def read_reg(self, reg_addr, reg_num):
        if reg_addr < 1008:
            function_code = 0x04
        else:
            function_code = 0x03
        data_to_send = [self.PowerSupply_addr, function_code] + self.decimal_to_hex_2bytes(reg_addr) \
                       + self.decimal_to_hex_2bytes(reg_num)

        data_to_send_crc = self.add_crc(data_to_send)
        self.write(bytearray(data_to_send_crc))

        # time.sleep(0.1)
        time.sleep(0.05)

        response = self.read()
        received_data = list(response)
        # print(f"Received {len(response)} bytes: "
        #       f"{'[{}]'.format(', '.join(f'0x{x:02X}' for x in received_data))}")
        return received_data

    def write_reg(self, reg_addr, reg_num, write_content):
        function_code = 0x10
        data_to_send = [self.PowerSupply_addr, function_code] + self.decimal_to_hex_2bytes(reg_addr) \
                       + self.decimal_to_hex_2bytes(reg_num) + [len(write_content)] + write_content
        data_to_send_crc = self.add_crc(data_to_send)
        # print('[{}]'.format(', '.join(f'0x{x:02X}' for x in data_to_send_crc)))
        self.write(bytearray(data_to_send_crc))
        
    def write(self, data):
        """写入数据到串口"""
        if self.serial_port_handler and self.serial_port_handler.is_open:
            try:
                bytes_written = self.serial_port_handler.write(bytearray(data))
            except Exception as e:
                print(f"写串口失败: {e}")
            else:
                pass
                # print(f"串口发送成功 {bytes_written} bytes: "
                #       f"{'[{}]'.format(', '.join(f'0x{x:02X}' for x in data))}")
        else:
            print("串口未打开，无法写入数据")
    '''
    从串口读取数据，缓冲区有多少，就读多少
    没有数据返回0，不阻塞
    '''
    def read(self):
        if self.serial_port_handler and self.serial_port_handler.is_open:
            try:
                bytes_available = self.serial_port_handler.in_waiting
                if bytes_available > 0:
                    received_data = self.serial_port_handler.read(bytes_available)
                else:
                    received_data = b''  # 或者处理没有数据的情况
                # received_data = self.serial_port_handler.read(bytes_available)
            except Exception as e:
                print(f"读串口失败: {e}")
            else:
                # received_data = list(received_data)
                # print(f"Received {len(received_data)} bytes: "
                #       f"{'[{}]'.format(', '.join(f'0x{x:02X}' for x in received_data))}")
                return received_data

                # print(f"读取数据: {data}")
                # return data.decode()
        else:
            print("串口未打开，无法读取数据")
            return None